import { isHuman } from '../../service';
import { isProduction } from '../../utils/check';
import { fetchSettings } from '../../utils/settings';
import gtag from '../analytics';
import { GtagEvent } from '../analytics/Event';

type RenderParams = {
    sitekey: string,
    callback?: (response: string) => void,
    theme?: 'dark' | 'light',
    tabindex?: number,
    'expired-callback'?: (response: any) => void,
    'error-callback'?: (response: any) => void,
}
type GrecaptchaType = {
    render: (domId: string, params: RenderParams) => number;
    reset: (id: number) => void;
    getResponse: (id: number) => void
}

// TODO: google recaptcha use React Component with Router
class Recaptcha {
    public static robotContainerId = 'google_robot'

    private src = 'https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit'
    private id = 'google_recaptcha'
    private sitekey = '6LdABcgpAAAAAJUgfxcN_ZgdEyeZhy7ezarB8hq-' //product '6LdABcgpAAAAAJUgfxcN_ZgdEyeZhy7ezarB8hq-' // test: 6LdrBcgpAAAAAAZmOxouwSUtKP6IzMgZybvDCWwT
    private robotContainerId = Recaptcha.robotContainerId
    private needCheck?: boolean
    private grecaptcha?: GrecaptchaType
    private flag: boolean = false;
    private callback?: (need: boolean) => void

    constructor(callback?: (need: boolean) => void) {
        if (!callback) {
            // if no callback, neet call 'init' after new Recaptcha()
            return
        }
        this.callback = callback;
        this.init();
        this.flag = true;
    }

    public async init(froce?: boolean): Promise<Recaptcha | undefined> {
        if (this.flag) {
            return
        }
        if (!isProduction()) {
            this.callback?.(false)
            return undefined
        }

        if (!froce) {
            const need = await this.check();
            if (!need) {
                this.callback?.(false)
                return undefined
            }
        }

        const success = await this.addScript();
        if (!success) {
            this.callback?.(false)
            return undefined
        }
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
        // @ts-ignore
        this.grecaptcha = window.grecaptcha;
        this.callback?.(true)
        return this
    }

    private addContainer() {
        if (!document.querySelector(`div#${this.robotContainerId}`)) {
            const div = document.createElement('div');
            div.id = this.robotContainerId;
            div.style.position = 'fixed'
            div.style.width = '100%'
            div.style.height = '100%'
            div.style.left = '0'
            div.style.top = '0'
            div.style.zIndex = '99'
            div.style.display = 'flex'
            div.style.justifyContent = 'center'
            div.style.alignItems = 'center'
            div.style.backgroundColor = '#011A38'
            document.body.appendChild(div);
        }
    }

    private addScript(): Promise<boolean> {
        return new Promise((resolve) => {
            if (!document.querySelector(`script#${this.id}`)) {
                const timeout = setTimeout(() => {
                    resolve(false)
                    gtag.event(GtagEvent.Error, { message: 'Recaptcha time out', type: 'timeout' })
                }, 15000); // timeout after 15s

                const script = document.createElement('script');
                script.src = this.src
                script.id = this.id;
                script.async = true;
                script.defer = true;

                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                window.onloadCallback = () => {
                    clearTimeout(timeout)
                    resolve(true)
                }

                script.onerror = () => {
                    clearTimeout(timeout)
                    resolve(false)
                }

                this.addContainer();
                document.head.appendChild(script);
                return
            }

            this.addContainer();
            resolve(true)
        })
    }

    private remove() {
        Recaptcha.removeRobotCheck();
    }

    private async check() {
        if (this.needCheck !== undefined) {
            return this.needCheck;
        }
        const result = await fetchSettings();
        this.needCheck = result.captcha;
        return this.needCheck;
    }

    // public
    public static removeRobotCheck() {
        try {
            const dom = document.getElementById(this.robotContainerId)
            if (dom) {
                document.body.removeChild(dom)
            }
        } catch (error) {
            // empty
        }
    }

    public render(cb: (success: boolean, reset: () => void) => void) {
        let id: number | undefined = undefined;
        const reset = () => {
            if (id !== undefined) {
                this.grecaptcha?.reset(id)
            }
        }
        const callback = async (response: string) => {
            if (!response) {
                cb(false, reset)
                return
            }
            const result = await isHuman({ response });
            if (result.data?.success) {
                cb(true, reset)
                this.remove()
                return
            }

            cb(false, reset)
        }

        id = this.grecaptcha?.render(this.robotContainerId, {
            sitekey: this.sitekey,
            theme: 'dark',
            callback
        })

        return id

    }
}

export default Recaptcha;